package cn.thr.ymcc.service.impl;

import cn.thr.ymcc.config.AlipayProperties;
import cn.thr.ymcc.constant.Cos;
import cn.thr.ymcc.domain.PayFlow;
import cn.thr.ymcc.domain.PayOrder;
import cn.thr.ymcc.domain.dto.ApplyDto;
import cn.thr.ymcc.dto.PayOrderDto;
import cn.thr.ymcc.dto.PaySuccessDto;
import cn.thr.ymcc.exception.GlobalException;
import cn.thr.ymcc.mapper.PayFlowMapper;
import cn.thr.ymcc.mapper.PayOrderMapper;
import cn.thr.ymcc.service.IPayOrderService;
import cn.thr.ymcc.util.AssertUtil;
import cn.thr.ymcc.util.StrUtils;
import com.alibaba.fastjson.JSON;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.facetoface.models.AlipayTradePrecreateResponse;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.checkerframework.checker.units.qual.C;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Map;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author thr
 * @since 2023-03-10
 */
@Service
public class PayOrderServiceImpl extends ServiceImpl<PayOrderMapper, PayOrder> implements IPayOrderService {

    @Autowired
    private PayFlowMapper payFlowMapper;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    /**
     * 保存支付单 ，与父接口中的save(PayOrder) 行程方法重载
     * @param payOrderDto
     */
    @Transactional
    @Override
    public void save(PayOrderDto payOrderDto) {
        PayOrder payOrder = new PayOrder();
        //拷贝实体类
        BeanUtils.copyProperties(payOrderDto,payOrder);
        //保存
        baseMapper.insert(payOrder);
    }

    @Autowired
    private AlipayProperties alipayProperties;
    /**
     * 申请支付
     * @param applyDto
     * @return html 标签
     */
    @Override
    public String apply(ApplyDto applyDto) {
        // 1. 设置参数（全局只需设置一次）
        Factory.setOptions(alipayProperties.getOptions());
        try {
            // 2. 发起API调用（以创建当面付收款二维码为例）
            AlipayTradePagePayResponse response = Factory.Payment
                    .Page().pay(applyDto.getSubject(), applyDto.getOrderNo(), applyDto.getPayAmount().toString(), applyDto.getCallUrl());
            // 3. 处理响应或异常
            if (ResponseChecker.success(response)) {
                //推迟延迟消息（30分钟之后执行），处理支付超时(订单服务中要修改订单状态，支付服务中要修改支付单状态)
                SendResult sendResult = rocketMQTemplate.syncSend(
                        Cos.pay.TOPIC_PAY_TIMEOUT + ":" + Cos.pay.TAGS_PAY_TIMEOUT,
                        MessageBuilder.withPayload(applyDto.getOrderNo()).build(),
                        3000,
                        3
                );
                AssertUtil.isTrue(sendResult.getSendStatus()==SendStatus.SEND_OK,"申请支付失败！");
                return response.getBody();
            } else {
                throw new GlobalException("调用失败，原因：" + response.getBody());
            }
        } catch (Exception e) {
            System.err.println("调用遭遇异常，原因：" + e.getMessage());
            throw new GlobalException("调用遭遇异常，原因：" + e.getMessage());
        }
    }
    /**
     * 完成后续业务
     * 1)修改支付单状态为已支付  --支付服务中
     * 2）添加支付流水记录       --支付服务中
     * 3）修改订单状态为已支付     --订单服务
     * 4）添加用户购买课程的记录    --课程服务
     * 第一二个操作都在当前支付服务中，可以直接操作，比较方便
     * 第二三个操作在订单服务中国，第四操作在课程服务中，这个需要涉及到跨数据库操作（分布式事务）
     *  1）可以使用openfeign 调用订单服务和课程服务的接口
     *  2）可以使用MQ 发消息（最终一致性），在订单服务和课程服务中分别写消费者
     */
    @Transactional
    @Override
    public void paySuccess(Map<String, String> parameters) {
        try {
            //订单编号
            String out_trade_no = parameters.get("out_trade_no");
            //根据订单编号去查数据
            PayOrder payOrder = baseMapper.selectOne(new QueryWrapper<PayOrder>()
                    .eq("order_no", out_trade_no));
            //1)修改支付单状态为已支付  --支付服务中
            payOrder.setPayStatus(1);
            baseMapper.updateById(payOrder);
            //2）添加支付流水记录       --支付服务中
            //字符串转日期
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            PayFlow payFlow = new PayFlow()
                    //日期
                    .setNotifyTime(sdf.parse(parameters.get("notify_time")))
                    .setSubject(parameters.get("subject"))
                    .setOutTradeNo(out_trade_no)
                    .setTotalAmount(new BigDecimal(parameters.get("receipt_amount")))
                    .setTradeStatus(parameters.get("trade_status"))
                    //额外参数
                    .setPassbackParams(payOrder.getExtParams())
                    .setPaySuccess(true)
                    .setResultDesc("支付成功");
            payFlowMapper.insert(payFlow);

            //3)推送消息 ： 对方需要什么就传什么
            //修改订单状态为已支付     --订单服务
            PaySuccessDto paySuccessDto = new PaySuccessDto();
            paySuccessDto.setOrderNo(out_trade_no);
            paySuccessDto.setLoginId(payOrder.getUserId());
            //拆分字符串
            String extParams = payOrder.getExtParams();
            //获取拆分的值
            String courseIds = StrUtils.getParamFromExtParams(extParams, "courseIds");
            //把字符串变成数组 字符串转换List的Long
            paySuccessDto.setCourseIds(StrUtils.splitStr2LongArr(courseIds));
            //发送消息
            SendResult sendResult = rocketMQTemplate.syncSend(
                    Cos.pay.TOPIC_PAY_SUCCESS + ":" + Cos.pay.TAGS_PAY_SUCCESS,
                    //转成JSON类型的字符串 发送过去
                    MessageBuilder.withPayload(JSON.toJSONString(paySuccessDto)).build()
            );
            AssertUtil.isTrue(sendResult.getSendStatus() == SendStatus.SEND_OK,"异步通知业务处理失败");
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
    /**
     * 支付超时处理
     * 修改支付单
     * @param orderNo
     */
    @Transactional
    @Override
    public void payTimeout(String orderNo) {
        //先查询支付单
        PayOrder payOrder = baseMapper.selectOne(new QueryWrapper<PayOrder>()
                .eq("order_no", orderNo));

        //判断如果支付单仍然时0 （待支付），就修改成4 （支付超时或者失败）
        if(payOrder.getPayStatus() == 0){
            payOrder.setPayStatus(4);
            baseMapper.updateById(payOrder);
        }
    }
}
